/*
 * Copyright 2011- Per Wendel
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package sardine.webserver;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import sardine.log.Logs;
import sardine.tpd.StaticResourceHanlder;

import java.util.Optional;

/**
 * @auth bruce-sha
 * @date 2015/5/21
 */
public class SardineServer {
    //
//    private static final int DEFAULT_PORT = 4567;
//    private static final String NAME = "Spark";
//    private Handler handler;
    private ChannelHandler handler;

    private EventLoopGroup workerGroup;
    private EventLoopGroup bossGroup;

//    private Server server;
//
//    private final Logger logger = LoggerFactory.getLogger(this.getClass());
//
//    public SparkServer(Handler handler) {
//        this.handler = handler;
//        System.setProperty("org.mortbay.log.class", "spark.JettyLogger");
//    }

    public SardineServer(ChannelHandler handler) {
        this.handler = handler;
//        System.setProperty("org.mortbay.log.class", "spark.JettyLogger");
    }

    //4

    /**
     * Ignites the spark server, listening on the specified port, running SSL secured with the specified keystore
     * and truststore.  If truststore is null, keystore is reused.
     *
     * @param host                The address to listen on
     * @param port                - the port
     * @param keystoreFile        - The keystore file location as string
     * @param keystorePassword    - the password for the keystore
     * @param truststoreFile      - the truststore file location as string, leave null to reuse keystore
     * @param truststorePassword  - the trust store password
     * @param staticFilesFolder   - the route to static files in classPath
     * @param externalFilesFolder - the route to static files external to classPath.
     */
    public void fire(final String host, final int port,
                     String keystoreFile, String keystorePassword,
                     String truststoreFile, String truststorePassword,
                     final Optional<String> staticFilesFolder, final Optional<String> externalFilesFolder) {

        if (staticFilesFolder.isPresent()) StaticResourceHanlder.staticFileFolder(staticFilesFolder.get());
        if (externalFilesFolder.isPresent()) StaticResourceHanlder.externalStaticFileFolder(externalFilesFolder.get());

        try {

            workerGroup = new NioEventLoopGroup();
            bossGroup = new NioEventLoopGroup();

            ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();

                            pipeline.addLast(new HttpServerCodec());
                            pipeline.addLast(new HttpObjectAggregator(1024 * 1024));
//                            pipeline.addLast( new HttpContentCompressor());//不能针对 chunked
//                            pipeline.addLast( new HttpChunkContentCompressor());//全部压缩
                            pipeline.addLast(new ChunkedWriteHandler());
                            pipeline.addLast(handler);
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            Channel channel = bootstrap.bind(host, port).sync().channel();

            Logs.log("service started on port {}", port);

            channel.closeFuture().sync();
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            stop();
        }
//        if (port == 0) {
//            try (ServerSocket s = new ServerSocket(0)) {
//                port = s.getLocalPort();
//            } catch (IOException e) {
//                logger.error("Could not singleton first available port (port set to 0), using default: {}", DEFAULT_PORT);
//                port = DEFAULT_PORT;
//            }
//        }
//
//        ServerConnector connector;
//
//        if (keystoreFile == null) {
//            connector = createSocketConnector();
//        } else {
//            connector = createSecureSocketConnector(keystoreFile,
//                                                    keystorePassword, truststoreFile, truststorePassword);
//        }
//
//        // Set some timeout OPTIONS to make debugging easier.
//        connector.setIdleTimeout(TimeUnit.HOURS.toMillis(1));
//        connector.setSoLingerTime(-1);
//        connector.setHost(host);
//        connector.setPort(port);
//
//        server = connector.getServer();
//        server.setConnectors(new Connector[] {connector});
//
//        // Handle static file routes
//        if (staticFilesFolder == null && externalFilesFolder == null) {
//            server.setHandler(handler);
//        } else {
//            List<Handler> handlersInList = new ArrayList<Handler>();
//            handlersInList.add(handler);
//
//            // Set static file location
//            setStaticFileLocationIfPresent(staticFilesFolder, handlersInList);
//
//            // Set external static file location
//            setExternalStaticFileLocationIfPresent(externalFilesFolder, handlersInList);
//
//            HandlerList handlers = new HandlerList();
//            handlers.setHandlers(handlersInList.toArray(new Handler[handlersInList.size()]));
//            server.setHandler(handlers);
//        }
//
//        try {
//            logger.info("== {} has ignited ...", NAME);
//            logger.info(">> Listening on {}:{}", host, port);
//
//            server.start();
//            server.join();
//        } catch (Exception e) {
//            logger.error("fire failed",e);
//            System.exit(100); // NOSONAR
//        }
    }

    //http://netty.io/wiki/user-guide-for-5.x.html#wiki-h3-17
    public void stop() {

        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();

//        logger.info(">>> {} shutting down ...", NAME);
//        try {
//            if (server != null) {
//                server.stop();
//            }
//        } catch (Exception e) {
//            logger.error("stop failed",e);
//            System.exit(100); // NOSONAR
//        }
//        logger.info("done");
    }
//
//    /**
//     * Creates a secure jetty socket connector. Keystore required, truststore
//     * optional. If truststore not specifed keystore will be reused.
//     *
//     * @param keystoreFile       The keystore file location as string
//     * @param keystorePassword   the password for the keystore
//     * @param truststoreFile     the truststore file location as string, leave null to reuse keystore
//     * @param truststorePassword the trust store password
//     * @return a secure socket connector
//     */
//    private static ServerConnector createSecureSocketConnector(String keystoreFile,
//                                                               String keystorePassword, String truststoreFile,
//                                                               String truststorePassword) {
//
//        SslContextFactory sslContextFactory = new SslContextFactory(
//                keystoreFile);
//
//        if (keystorePassword != null) {
//            sslContextFactory.setKeyStorePassword(keystorePassword);
//        }
//        if (truststoreFile != null) {
//            sslContextFactory.setTrustStorePath(truststoreFile);
//        }
//        if (truststorePassword != null) {
//            sslContextFactory.setTrustStorePassword(truststorePassword);
//        }
//        return new ServerConnector(new Server(), sslContextFactory);
//    }
//
//    /**
//     * Creates an ordinary, non-secured Jetty server connector.
//     *
//     * @return - a server connector
//     */
//    private static ServerConnector createSocketConnector() {
//        return new ServerConnector(new Server());
//    }
//
//    /**
//     * Sets static file location if present
//     */
//    private static void setStaticFileLocationIfPresent(String staticFilesRoute, List<Handler> handlersInList) {
//        if (staticFilesRoute != null) {
//            ResourceHandler resourceHandler = new ResourceHandler();
//            Resource staticResources = Resource.newClassPathResource(staticFilesRoute);
//            resourceHandler.setBaseResource(staticResources);
//            resourceHandler.setWelcomeFiles(new String[] {"index.html"});
//            handlersInList.add(resourceHandler);
//        }
//    }
//
//    /**
//     * Sets external static file location if present
//     */
//    private static void setExternalStaticFileLocationIfPresent(String externalFilesRoute,
//                                                               List<Handler> handlersInList) {
//        if (externalFilesRoute != null) {
//            try {
//                ResourceHandler externalResourceHandler = new ResourceHandler();
//                Resource externalStaticResources = Resource.newResource(new File(externalFilesRoute));
//                externalResourceHandler.setBaseResource(externalStaticResources);
//                externalResourceHandler.setWelcomeFiles(new String[] {"index.html"});
//                handlersInList.add(externalResourceHandler);
//            } catch (IOException exception) {
//                exception.printStackTrace(); // NOSONAR
//                System.err.println("Error during initialize external resource " + externalFilesRoute); // NOSONAR
//            }
//        }
//    }

}
